use super::{
    err, err_value, json, ok, throw_err, Base, Client, Collect, ECode, KeyNode, Level, Lock, Resp,
    SvCache, VBase, VMap, Value,
};

pub struct Tech;

impl Base for Tech {}

impl Tech {
    pub async fn do_cmd(
        &self,
        model: &str,
        para: &Value,
        cmd: &str,
        client: &Client,
        cache: SvCache,
        lock: Lock,
    ) -> Resp {
        let data = para.ckobj()?;
        let line = data.ck_str("line")?;

        let collect = Collect::new(client, cache, line);

        match model {
            "tech" => match cmd {
                "rm" => {
                    let sn = data.ck_str("k")?;

                    let k_all = collect.key_all("tech")?;
                    let k_tech = collect.key_sn("tech", sn)?;
                    let k_bom = collect.key_sub_sn("tech", "bom", sn)?;
                    let k_esop = collect.key_sub_sn("tech", "esop", sn)?;

                    let _ = lock.lock().await;

                    let mut put = Vec::new();
                    let mut del = vec![k_tech.to_string()];

                    let help = Level::new("tech", &collect);

                    help.rm_year_item(&sn, &mut put, &mut del).await?;

                    collect.del_mass_item(&k_all, sn, &mut put, &mut del);
                    collect.del_mass(None, &k_bom, &mut del).await?;
                    collect.del_mass(None, &k_esop, &mut del).await?;

                    collect.commit(Some(&put), Some(&del)).await?;

                    ok!()
                }
                "list" => {
                    let help = Level::new("tech", &collect);
                    let body = help.list_year().await?;
                    ok!(body)
                }
                "add" => {
                    let val = data.ck_obj("v")?;
                    let sn = val.ck_str_empty_trim("sn")?;
                    let k_tech = collect.key_sn("tech", &sn)?;

                    let _ = lock.lock().await;

                    let y = base::cur_time("%Y");
                    let pstart = base::cur_time("%Y-%m-%d 00:00:00");

                    let mut tmp = val.clone();
                    tmp.insert("pstart".to_string(), json!(pstart));

                    let mut put = vec![(k_tech, json!(tmp))];

                    let help = Level::new("tech", &collect);
                    help.add_sn_to_year(&y, &sn, &mut put)?;

                    collect.commit(Some(&put), None).await?;

                    ok!()
                }
                "madd" => {
                    let _ = lock.lock().await;
                    let v_data = data.ck_array("v")?;

                    let y = base::cur_time("%Y");
                    let pstart = base::cur_time("%Y-%m-%d 00:00:00");
                    let help = Level::new("tech", &collect);

                    let mut put = Vec::new();
                    let mut y_put = Vec::new();

                    for val in v_data.iter() {
                        let val = val.ckobj()?;

                        let sn = val.ck_str_empty_trim("sn")?;
                        let k_tech = collect.key_sn("tech", &sn)?;

                        if let Ok(_) = collect.item(&k_tech).await {
                            collect.merge(&k_tech, &val, &mut put)?;
                        } else {
                            let mut tmp = val.clone();
                            tmp.insert("pstart".to_string(), json!(pstart));

                            put.push((k_tech, json!(tmp)));
                            y_put.push(json!(sn));
                        }
                    }

                    help.add_vec_to_year(&y, &y_put, &mut put)?;

                    collect.commit(Some(&put), None).await?;

                    ok!()
                }
                _ => collect.do_cmd("tech", cmd, para, lock, None).await,
            },
            "esop" | "bom" => match cmd {
                "list" => {
                    let tech_sn = data.ck_str("k")?;
                    let dsn = collect.key_sub_sn("tech", model, tech_sn)?;

                    let body = collect.list_array_ext("tech", model, &dsn, tech_sn).await?;
                    ok!(body)
                }
                "add" => {
                    let val = data.ck_obj("v")?;
                    let tech_sn = data.ck_str("k")?;
                    let k_sn = collect.key_sub_sn("tech", model, tech_sn)?;

                    let _ = lock.lock().await;

                    collect.add_set_to_mass(&k_sn, val).await?;

                    ok!()
                }
                "rmall" => {
                    let tech_sn = data.ck_str("k")?;
                    let k_sn = collect.key_sub_sn("tech", model, tech_sn)?;

                    let _ = lock.lock().await;

                    let mut del = Vec::new();
                    collect.del_mass(None, &k_sn, &mut del).await?;

                    collect.commit(None, Some(&del)).await?;

                    ok!()
                }
                "rm" => {
                    let wsn = data.ck_str("sn")?;

                    let tech_sn = data.ck_str("k")?;
                    let k_sn = collect.key_sub_sn("tech", model, tech_sn)?;
                    let k_item = collect.key_sub_sn_ext("tech", model, tech_sn, wsn)?;

                    let _ = lock.lock().await;

                    let mut put = Vec::new();
                    let mut del = vec![k_item.to_string()];

                    collect.del_mass_item(&k_sn, wsn, &mut put, &mut del);

                    collect.commit(Some(&put), Some(&del)).await?;

                    ok!()
                }
                "md" => {
                    let wsn = data.ck_str("sn")?;
                    let val = data.ck_val("v")?;
                    let tech_sn = data.ck_str("k")?;
                    let k_sn = collect.key_sub_sn("tech", model, tech_sn)?;

                    let k_item = collect.key_sub_sn_ext("tech", model, tech_sn, wsn)?;

                    let _ = lock.lock().await;

                    let mut put = vec![(k_item, val.clone())];

                    collect.merge_mass(&k_sn, wsn, &mut put);

                    collect.commit(Some(&put), None).await?;

                    ok!()
                }
                "get" => {
                    let tech_sn = data.ck_str("k")?;
                    let wsn = data.ck_str("sn")?;

                    let k_item = collect.key_sub_sn_ext("tech", model, tech_sn, wsn)?;

                    let body = collect.item(&k_item).await?;
                    ok!(body)
                }
                _ => throw_err!(ECode::NoServe, format!("no {cmd} serve")),
            },
            _ => throw_err!(ECode::NoServe, "no tech serve"),
        }
    }
}
